﻿/***************************************************************************
 * 文 件 名:   HK_TargetCamCtrl
 * 
 * 描    述:   Camera多目标切换管理工具，摄像机控制脚本
 * 
 * 版    本：  V1.1
 * 创 建 者：  京产肠饭、谭潇杰
 * 创建时间：  2023/5/9
 * 链   接：  https://gitee.com/JingChanChangFan/hk_-unity-tools
****************************************************************************/

using UnityEngine;
using UnityEngine.EventSystems;
using QFramework;
using DG.Tweening;

namespace HKTools
{
    [RequireComponent(typeof(Camera))]
    public class HK_TargetCamCtrl : HK_TargetCamController
    {
        IHK_TargetCamSystem targetCamSystem;

        [Header("初始化目标：")]
        public HK_TargetEnum initTarget;

        [Header("Rot时，旋转速度和缩放速度：")]
        public float moveSpeed = 2f;
        public float wheelSpeed = 4f;

        [Header("摄像机的最远距离和最近距离：")]
        public float minDist = 3;
        public float maxDist = 60;

        [Header("左边和右边视角范围：")]
        public float leftRot;
        public float rightRot;

        float nowCamEulerY;

        [Header("最大和最小俯仰角：")]
        public float maxAngle = 50;

        [Header("相机的Mask"), SerializeField] GameObject mask_Go;

        // 当前摄像机俯仰角
        float nowCamEulerX;
        float MaxAngle
        {
            get
            {
                if (maxAngle > 90)
                    maxAngle = 90;

                return maxAngle;
            }
        }

        public float minAngle = 0;
        float MinAngle
        {
            get
            {
                if (minAngle <= -90)
                    minAngle = -89.8f;

                return minAngle;
            }
        }

        Vector2 xLimitPos, zLimitPos;

        public bool isAreaCamForward;

        void Awake()
        {
            // 获取 IHK_TargetCamSystem 系统层
            targetCamSystem = this.GetSystem<IHK_TargetCamSystem>();

            // 初始化 被控制的 Camera
            targetCamSystem.InitCamera(this);

            // 注册 切换至 RotTarget目标 事件
            this.RegisterEvent<Event_SwitchToRotTarget>(args =>
            {
                if (args._moveSpeed != -1)
                {
                    moveSpeed = args._moveSpeed;
                }

                if (args._wheelSpeed != -1)
                {
                    wheelSpeed = args._wheelSpeed;
                }

                mouseLerpPos = Vector2.zero;
                wheelTargetValue = 0;

                //摄像机视角
                leftRot = args._rotTargetItem.leftRot;
                rightRot = args._rotTargetItem.rightRot;

                //缩放最远最近距离
                minDist = args._rotTargetItem.minDis;
                maxDist = args._rotTargetItem.maxDis;

            }).UnRegisterWhenGameObjectDestroyed(gameObject);

            // 注册 切换至 AreaTarget目标 事件
            this.RegisterEvent<Event_SwitchToAreaTarget>(args =>
            {
                if (Vector3.Dot(args._areaTargetItem.transform.forward, Vector3.forward) > 0)
                {
                    isAreaCamForward = false;
                }
                else
                {
                    isAreaCamForward = true;
                }

                if (args._moveSpeed != -1)
                {
                    moveSpeed = args._moveSpeed;
                }

                if (args._wheelSpeed != -1)
                {
                    wheelSpeed = args._wheelSpeed;
                }

                xLimitPos = new Vector2(args._areaTargetItem.targetPos.x - args._areaTargetItem.areaWidth / 2, args._areaTargetItem.targetPos.x + args._areaTargetItem.areaWidth / 2);

                zLimitPos = new Vector2(args._areaTargetItem.targetPos.z - args._areaTargetItem.areaLength / 2, args._areaTargetItem.targetPos.z + args._areaTargetItem.areaLength / 2);

                mouseLerpPos = Vector2.zero;
                wheelTargetValue = 0;

                targetX = args._areaTargetItem.targetPos.x;
                targetZ = args._areaTargetItem.targetPos.z;

            }).UnRegisterWhenGameObjectDestroyed(gameObject);

            // 注册 切换至 FollowTarget目标 事件
            this.RegisterEvent<Event_SwitchToFollowTarget>(args =>
            {
                if (args._moveSpeed != -1)
                {
                    moveSpeed = args._moveSpeed;
                }

                if (args._wheelSpeed != -1)
                {
                    wheelSpeed = args._wheelSpeed;
                }

                mouseLerpPos = Vector2.zero;
                wheelTargetValue = 0;

                minDist = args._followTargetItem.minDis;
                maxDist = args._followTargetItem.maxDis;

                lastFollowTargetPos = args._followTargetItem.transform.position;
            }).UnRegisterWhenGameObjectDestroyed(gameObject);
        }

        void Start()
        {
            nowCamEulerX = transform.localEulerAngles.x;

            if (initTarget != HK_TargetEnum.None)
            {
                this.SendCommand(new CMD_ChangeCamTarget(initTarget));
            }
        }

        void LateUpdate()
        {
            if (!targetCamSystem.IsCanControlCam || targetCamSystem.CurTarget == null)
                return;

            if (targetCamSystem.CamMoveType.Value == MoveType.Rot)
            {
                OnRotTargetMoveCtrl();
                OnRotTargetFOVCtrl();
            }

            if (targetCamSystem.CamMoveType.Value == MoveType.Area)
            {
                OnAreaTargetMoveCtrl();
                OnAreaTargetFOVCtrl();
            }

            if (targetCamSystem.CamMoveType.Value == MoveType.Follow)
            {
                OnFollowTargetMoveCtrl();
                OnRotTargetFOVCtrl();
            }
        }

        Tweener fadeIn;
        Tweener fadeOut;

        public void ShowMask()
        {
            if (fadeIn != null && fadeIn.IsPlaying())
                fadeIn.Kill();

            if (fadeOut != null && fadeOut.IsPlaying())
                fadeOut.Kill();

            mask_Go.SetActive(true);
            fadeIn = mask_Go.GetComponent<Renderer>().material.DOFade(1, 0.4f);

            fadeOut = mask_Go.GetComponent<Renderer>().material.DOFade(0, 0.4f).SetDelay(0.4f).OnComplete(() => mask_Go.SetActive(false));
        }

        Vector2 mousePos;
        Vector2 mouseLerpPos;
        float targetX, targetZ;

        float wheelValue;
        float wheelTargetValue;

        Touch oldTouch1;
        Touch oldTouch2;

        Vector3 lastFollowTargetPos;
        Vector3 offset;

        /// <summary>
        /// FollowTarget时，Camera移动控制
        /// </summary>
        void OnFollowTargetMoveCtrl()
        {
            if (lastFollowTargetPos == Vector3.zero)
                lastFollowTargetPos = targetCamSystem.CurTarget.transform.position;

            offset = targetCamSystem.CurTarget.transform.position - lastFollowTargetPos;

            if (offset.magnitude != 0)
            {
                Debug.Log(targetCamSystem.CurTarget.transform.position);
                transform.position += offset;
            }

            lastFollowTargetPos = targetCamSystem.CurTarget.transform.position;

            mousePos.x = Input.GetAxis("Mouse X");
            mousePos.y = Input.GetAxis("Mouse Y");

#if UNITY_STANDALONE_WIN || UNITY_EDITOR || UNITY_WEBGL
            if (Input.GetMouseButton(0))
            {
                if (EventSystem.current != null)
                {
                    if (!EventSystem.current.IsPointerOverGameObject())
                    {
                        mouseLerpPos.x = Mathf.Lerp(mouseLerpPos.x, mousePos.x, 5 * Time.deltaTime);
                        mouseLerpPos.y = Mathf.Lerp(mouseLerpPos.y, mousePos.y, 5 * Time.deltaTime);
                    }
                }
                else
                {
                    mouseLerpPos.x = Mathf.Lerp(mouseLerpPos.x, mousePos.x, 5 * Time.deltaTime);
                    mouseLerpPos.y = Mathf.Lerp(mouseLerpPos.y, mousePos.y, 5 * Time.deltaTime);
                }
            }
            else
            {
                mouseLerpPos.x = Mathf.Lerp(mouseLerpPos.x, 0, 5 * Time.deltaTime);
                mouseLerpPos.y = Mathf.Lerp(mouseLerpPos.y, 0, 5 * Time.deltaTime);
            }
#endif

#if UNITY_ANDROID
        if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
        {
            if (EventSystem.current != null)
            {
                if (!EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
                {
                    mouseLerpPos.x = Mathf.Lerp(mouseLerpPos.x, mousePos.x, 5 * Time.deltaTime);
                    mouseLerpPos.y = Mathf.Lerp(mouseLerpPos.y, mousePos.y, 5 * Time.deltaTime);
                }
            }
            else
            {
                mouseLerpPos.x = Mathf.Lerp(mouseLerpPos.x, mousePos.x, 5 * Time.deltaTime);
                mouseLerpPos.y = Mathf.Lerp(mouseLerpPos.y, mousePos.y, 5 * Time.deltaTime);
            }
        }
        else
        {
            mouseLerpPos.x = Mathf.Lerp(mouseLerpPos.x, 0, 5 * Time.deltaTime);
            mouseLerpPos.y = Mathf.Lerp(mouseLerpPos.y, 0, 5 * Time.deltaTime);
        }
#endif

            transform.RotateAround(targetCamSystem.CurTarget.transform.position, Vector3.up, mouseLerpPos.x * moveSpeed);

            nowCamEulerX = transform.localEulerAngles.x > 90 ? transform.localEulerAngles.x - 360 : transform.localEulerAngles.x;

            if (nowCamEulerX > MaxAngle || Vector3.Dot(transform.up, Vector3.up) < 0)
            {
                if (mouseLerpPos.y < 0)
                    mouseLerpPos.y = 0;
            }

            if (nowCamEulerX < MinAngle)
            {
                if (mouseLerpPos.y > 0)
                    mouseLerpPos.y = 0;
            }

            if (Mathf.Abs(-mouseLerpPos.y * moveSpeed) < 0.02f)
                return;

            transform.RotateAround(targetCamSystem.CurTarget.transform.position, transform.right, -mouseLerpPos.y * moveSpeed * 0.5f);
        }

        /// <summary>
        /// RotTarget时，Camera移动控制
        /// </summary>
        void OnRotTargetMoveCtrl()
        {
            mousePos.x = Input.GetAxis("Mouse X");
            mousePos.y = Input.GetAxis("Mouse Y");

#if UNITY_STANDALONE_WIN || UNITY_EDITOR || UNITY_WEBGL
            if (Input.GetMouseButton(0))
            {
                if (EventSystem.current != null)
                {
                    if (!EventSystem.current.IsPointerOverGameObject())
                    {
                        mouseLerpPos.x = Mathf.Lerp(mouseLerpPos.x, mousePos.x, 5 * Time.deltaTime);
                        mouseLerpPos.y = Mathf.Lerp(mouseLerpPos.y, mousePos.y, 5 * Time.deltaTime);
                    }
                }
                else
                {
                    mouseLerpPos.x = Mathf.Lerp(mouseLerpPos.x, mousePos.x, 5 * Time.deltaTime);
                    mouseLerpPos.y = Mathf.Lerp(mouseLerpPos.y, mousePos.y, 5 * Time.deltaTime);
                }
            }
            else
            {
                mouseLerpPos.x = Mathf.Lerp(mouseLerpPos.x, 0, 5 * Time.deltaTime);
                mouseLerpPos.y = Mathf.Lerp(mouseLerpPos.y, 0, 5 * Time.deltaTime);
            }
#endif

#if UNITY_ANDROID
        if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
        {
            if (EventSystem.current != null)
            {
                if (!EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
                {
                    mouseLerpPos.x = Mathf.Lerp(mouseLerpPos.x, mousePos.x, 5 * Time.deltaTime);
                    mouseLerpPos.y = Mathf.Lerp(mouseLerpPos.y, mousePos.y, 5 * Time.deltaTime);
                }
            }
            else
            {
                mouseLerpPos.x = Mathf.Lerp(mouseLerpPos.x, mousePos.x, 5 * Time.deltaTime);
                mouseLerpPos.y = Mathf.Lerp(mouseLerpPos.y, mousePos.y, 5 * Time.deltaTime);
            }
        }
        else
        {
            mouseLerpPos.x = Mathf.Lerp(mouseLerpPos.x, 0, 5 * Time.deltaTime);
            mouseLerpPos.y = Mathf.Lerp(mouseLerpPos.y, 0, 5 * Time.deltaTime);
        }
#endif
            /*************************视角范围**********************************/

            if (leftRot != 0 || rightRot != 0)
            {
                if (leftRot == 180)
                    leftRot = -178;

                if (rightRot == 180)
                    rightRot = 178;

                float angle = Vector3.Angle(Vector3.ProjectOnPlane(targetCamSystem.CurTarget.transform.forward, Vector3.up),
                         Vector3.ProjectOnPlane(transform.position - targetCamSystem.CurTarget.transform.position, Vector3.up));

                if (angle != 0)
                {
                    // Cam 在 Target 右侧
                    if (Vector3.Cross(targetCamSystem.CurTarget.transform.forward, transform.forward).y < 0)
                    {
                        if (angle > rightRot)
                        {
                            if (mouseLerpPos.x > 0)
                                mouseLerpPos.x = 0;
                        }
                    }
                    else // Cam 在 Target 左侧
                    {
                        if (angle > leftRot)
                        {
                            if (mouseLerpPos.x < 0)
                                mouseLerpPos.x = 0;
                        }
                    }
                }
            }

            /******************************************************************/

            transform.RotateAround(targetCamSystem.CurTarget.transform.position, Vector3.up, mouseLerpPos.x * moveSpeed);

            nowCamEulerX = transform.localEulerAngles.x > 90 ? transform.localEulerAngles.x - 360 : transform.localEulerAngles.x;

            if (nowCamEulerX > MaxAngle || Vector3.Dot(transform.up, Vector3.up) < 0)
            {
                if (mouseLerpPos.y < 0)
                    mouseLerpPos.y = 0;
            }

            if (nowCamEulerX < MinAngle)
            {
                if (mouseLerpPos.y > 0)
                    mouseLerpPos.y = 0;
            }

            if (Mathf.Abs(-mouseLerpPos.y * moveSpeed) < 0.02f)
                return;

            transform.RotateAround(targetCamSystem.CurTarget.transform.position, transform.right, -mouseLerpPos.y * moveSpeed * 0.5f);
        }

        /// <summary>
        /// RotTarget时，Camera缩放控制
        /// </summary>
        void OnRotTargetFOVCtrl()
        {
#if UNITY_STANDALONE_WIN || UNITY_EDITOR || UNITY_WEBGL
            wheelValue = Input.GetAxis("Mouse ScrollWheel");

            if (Vector3.Distance(transform.position, targetCamSystem.CurTarget.transform.position) < minDist && wheelValue > 0)
                return;

            if (Vector3.Distance(transform.position, targetCamSystem.CurTarget.transform.position) > maxDist && wheelValue < 0)
                return;

            if (wheelValue > 0)
                wheelValue = 0.15f;

            if (wheelValue < 0)
                wheelValue = -0.15f;

            wheelTargetValue = Mathf.Lerp(wheelTargetValue, wheelValue, 20 * Time.deltaTime);

            transform.Translate(Vector3.forward * wheelTargetValue * wheelSpeed);
#endif

#if UNITY_ANDROID
        if (Input.touchCount > 1)
        {
            Touch newTouch1 = Input.GetTouch(0);
            Touch newTouch2 = Input.GetTouch(1);

            if (newTouch2.phase == TouchPhase.Began)
            {
                oldTouch1 = newTouch1;
                oldTouch2 = newTouch2;
                wheelTargetValue = 0;
                return;
            }

            if (newTouch1.phase == TouchPhase.Moved || newTouch2.phase == TouchPhase.Moved)
            {
                float oldDistance = Vector2.Distance(oldTouch1.position, oldTouch2.position);
                float newDistance = Vector2.Distance(newTouch1.position, newTouch2.position);

                if ((newDistance - oldDistance) < 0)
                    wheelValue = -0.15f;
                else if ((newDistance - oldDistance) > 0)
                    wheelValue = 0.15f;

                if (Vector3.Distance(transform.position, targetCamSystem.CurTarget .transform.position) < minDist && wheelValue > 0)
                    return;

                if (Vector3.Distance(transform.position,targetCamSystem.CurTarget .transform.position) > maxDist && wheelValue < 0)
                    return;

                wheelTargetValue = Mathf.Lerp(wheelTargetValue, wheelValue, 20 * Time.deltaTime);

                transform.Translate(Vector3.forward * wheelTargetValue * wheelSpeed);

                oldTouch1 = newTouch1;
                oldTouch2 = newTouch2;
            }
        }
#endif
        }

        /// <summary>
        /// AreaTarget时，Camera移动控制
        /// </summary>
        void OnAreaTargetMoveCtrl()
        {
#if UNITY_STANDALONE_WIN || UNITY_EDITOR || UNITY_WEBGL
            mousePos.x = Input.GetAxis("Mouse X");
            mousePos.y = Input.GetAxis("Mouse Y");

            if (Input.GetMouseButton(0))
            {
                if (isAreaCamForward)
                {
                    if ((targetX < xLimitPos.x && mousePos.x > 0) || (targetX > xLimitPos.y && mousePos.x < 0))
                    {
                        mousePos.x = 0;
                    }
                    else
                    {
                        targetX -= mousePos.x * Time.deltaTime * moveSpeed * 20;
                    }

                    if ((targetZ < zLimitPos.x && mousePos.y > 0) || (targetZ > zLimitPos.y && mousePos.y < 0))
                    {
                        mousePos.y = 0;
                    }
                    else
                    {
                        targetZ -= mousePos.y * Time.deltaTime * moveSpeed * 20;
                    }
                }
                else
                {
                    if ((targetX < xLimitPos.x && mousePos.x < 0) || (targetX > xLimitPos.y && mousePos.x > 0))
                        return;

                    targetX += mousePos.x * Time.deltaTime * moveSpeed * 20;

                    if ((targetZ < zLimitPos.x && mousePos.y < 0) || (targetZ > zLimitPos.y && mousePos.y > 0))
                        return;

                    targetZ += mousePos.y * Time.deltaTime * moveSpeed * 20;
                }
            }
            transform.position = Vector3.Lerp(transform.position, new Vector3(targetX, transform.position.y, targetZ), Time.deltaTime * 20);

#endif

#if UNITY_ANDROID

        if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
        {
            mousePos.x = Input.GetTouch(0).deltaPosition.x;
            mousePos.y = Input.GetTouch(0).deltaPosition.y;

            if (isAreaCamForward)
            {
                if ((targetX < xLimitPos.x && mousePos.x > 0) || (targetX > xLimitPos.y && mousePos.x < 0))
                {
                    mousePos.x = 0;
                }
                else
                {
                    targetX -= mousePos.x * Time.deltaTime * moveSpeed;
                }

                if ((targetZ < zLimitPos.x && mousePos.y > 0) || (targetZ > zLimitPos.y && mousePos.y < 0))
                {
                    mousePos.y = 0;
                }
                else
                {
                    targetZ -= mousePos.y * Time.deltaTime * moveSpeed;
                }
            }
            else
            {
                if ((targetX < xLimitPos.x && mousePos.x < 0) || (targetX > xLimitPos.y && mousePos.x > 0))
                    return;

                targetX += mousePos.x * Time.deltaTime * moveSpeed;

                if ((targetZ < zLimitPos.x && mousePos.y < 0) || (targetZ > zLimitPos.y && mousePos.y > 0))
                    return;

                targetZ += mousePos.y * Time.deltaTime * moveSpeed;
            }
        }
        transform.position = Vector3.Lerp(transform.position, new Vector3(targetX, transform.position.y, targetZ), Time.deltaTime*10f);
#endif
        }

        /// <summary>
        /// AreaTarget时，Camera缩放控制 
        /// </summary>
        void OnAreaTargetFOVCtrl()
        {
#if UNITY_STANDALONE_WIN || UNITY_EDITOR || UNITY_WEBGL
            wheelValue = Input.GetAxis("Mouse ScrollWheel");

            if (wheelValue > 0)
                wheelValue = 1f;

            if (wheelValue < 0)
                wheelValue = -1f;

            wheelTargetValue = Mathf.Lerp(wheelTargetValue, wheelValue, wheelSpeed * Time.deltaTime);

            if (Camera.main.fieldOfView > 85 && wheelTargetValue < 0)
                return;

            if (Camera.main.fieldOfView < 50 && wheelTargetValue > 0)
                return;

            Camera.main.fieldOfView -= wheelTargetValue;
#endif

#if UNITY_ANDROID
        if (Input.touchCount > 1)
        {
            Touch newTouch1 = Input.GetTouch(0);
            Touch newTouch2 = Input.GetTouch(1);

            if (newTouch2.phase == TouchPhase.Began)
            {
                oldTouch1 = newTouch1;
                oldTouch2 = newTouch2;
                wheelTargetValue = 0;
                return;
            }

            if (newTouch1.phase == TouchPhase.Moved || newTouch2.phase == TouchPhase.Moved)
            {
                float oldDistance = Vector2.Distance(oldTouch1.position, oldTouch2.position);
                float newDistance = Vector2.Distance(newTouch1.position, newTouch2.position);

                if ((newDistance - oldDistance) < 0)
                    wheelValue = -1f;
                else if ((newDistance - oldDistance) > 0)
                    wheelValue = 1f;

                wheelTargetValue = Mathf.Lerp(wheelTargetValue, wheelValue, wheelSpeed * Time.deltaTime);
                
                if (Camera.main.fieldOfView > 85 && wheelTargetValue < 0)
                    return;

                if (Camera.main.fieldOfView < 50 && wheelTargetValue > 0)
                    return;
                Camera.main.fieldOfView -= wheelTargetValue;

                oldTouch1 = newTouch1;
                oldTouch2 = newTouch2;
            }
        }
#endif
        }
    }
}